}
bool FileSystem::setFolderPermissions(const QString &path,
- FileSystem::FolderPermissions permissions) noexcept
+ FileSystem::FolderPermissions permissions,
+ bool * const permissionsChanged) noexcept
{
+ bool permissionsDidChange = false;
+
+ if (permissionsChanged) {
+ *permissionsChanged = false;
+ }
+
#ifdef Q_OS_WIN
SECURITY_INFORMATION info = DACL_SECURITY_INFORMATION;
std::unique_ptr<char[]> securityDescriptor;
qCWarning(lcFileSystem) << "error when calling SetFileSecurityW" << QDir::toNativeSeparators(path) << GetLastError();
return false;
}
+
+ permissionsDidChange = true;
#else
static constexpr auto writePerms = std::filesystem::perms::owner_write | std::filesystem::perms::group_write | std::filesystem::perms::others_write;
const auto stdStrPath = path.toStdWString();
+
+ const auto currentPermissions = std::filesystem::status(stdStrPath).permissions();
+ qCDebug(lcFileSystem()).nospace() << "current permissions path=" << path << " perms=" << Qt::showbase << Qt::oct << static_cast<int>(currentPermissions);
+
try
{
switch (permissions) {
- case OCC::FileSystem::FolderPermissions::ReadOnly:
- std::filesystem::permissions(stdStrPath, writePerms, std::filesystem::perm_options::remove);
+ case OCC::FileSystem::FolderPermissions::ReadOnly: {
+ qCDebug(lcFileSystem()).nospace() << "ensuring folder is read only path=" << path;
+
+ if ((currentPermissions & writePerms) != std::filesystem::perms::none) {
+ qCDebug(lcFileSystem()).nospace() << "removing owner/group/others write permissions path=" << path;
+ std::filesystem::permissions(stdStrPath, writePerms, std::filesystem::perm_options::remove);
+ permissionsDidChange = true;
+ }
+
break;
- case OCC::FileSystem::FolderPermissions::ReadWrite:
+ }
+ case OCC::FileSystem::FolderPermissions::ReadWrite: {
+ qCDebug(lcFileSystem()).nospace() << "ensuring folder is read/writable path=" << path;
+
+ if ((currentPermissions & std::filesystem::perms::others_write) != std::filesystem::perms::none) {
+ qCDebug(lcFileSystem()).nospace() << "removing others write permissions path=" << path;
+ std::filesystem::permissions(stdStrPath, std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
+ permissionsDidChange = true;
+ }
+
+ if ((currentPermissions & std::filesystem::perms::owner_write) == std::filesystem::perms::none) {
+ qCDebug(lcFileSystem()).nospace() << "adding owner write permissions path=" << path;
+ std::filesystem::permissions(stdStrPath, std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
+ permissionsDidChange = true;
+ }
+
break;
}
+ }
}
catch (const std::filesystem::filesystem_error &e)
{
return false;
}
- try
- {
- switch (permissions) {
- case OCC::FileSystem::FolderPermissions::ReadOnly:
- break;
- case OCC::FileSystem::FolderPermissions::ReadWrite:
- std::filesystem::permissions(stdStrPath, std::filesystem::perms::others_write, std::filesystem::perm_options::remove);
- std::filesystem::permissions(stdStrPath, std::filesystem::perms::owner_write, std::filesystem::perm_options::add);
- break;
- }
- }
- catch (const std::filesystem::filesystem_error &e)
- {
- qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << e.path1().c_str() << e.path2().c_str();
- return false;
- }
- catch (const std::system_error &e)
- {
- qCWarning(lcFileSystem()) << "exception when modifying folder permissions" << e.what() << "- path:" << stdStrPath;
- return false;
- }
- catch (...)
- {
- qCWarning(lcFileSystem()) << "exception when modifying folder permissions - path:" << stdStrPath;
- return false;
+ if (permissionsDidChange) {
+ const auto newPermissions = std::filesystem::status(stdStrPath).permissions();
+ qCDebug(lcFileSystem()).nospace() << "updated permissions path=" << path << " perms=" << Qt::showbase << Qt::oct << static_cast<int>(newPermissions);
}
#endif
+ if (permissionsChanged) {
+ *permissionsChanged = permissionsDidChange;
+ }
+
return true;
}
--- /dev/null
+/*
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: CC0-1.0
+ *
+ * This software is in the public domain, furnished "as is", without technical
+ * support, and with no warranty, express or implied, as to its usefulness for
+ * any purpose.
+ */
+
+#include <QtTest>
+#include <QTemporaryDir>
+
+#include "common/filesystembase.h"
+#include "logger.h"
+
+#include "libsync/filesystem.h"
+
+using namespace OCC;
+namespace std_fs = std::filesystem;
+
+class TestFileSystem : public QObject
+{
+ Q_OBJECT
+
+private:
+ QTemporaryDir testDir;
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ OCC::Logger::instance()->setLogFlush(true);
+ OCC::Logger::instance()->setLogDebug(true);
+
+ QStandardPaths::setTestModeEnabled(true);
+
+ QDir dir(testDir.path());
+ dir.mkdir("existingDirectory");
+ }
+
+#ifndef Q_OS_WIN
+ void testSetFolderPermissionsExistingDirectory_data()
+ {
+ constexpr auto perms_0555 =
+ std_fs::perms::owner_read | std_fs::perms::owner_exec
+ | std_fs::perms::group_read | std_fs::perms::group_exec
+ | std_fs::perms::others_read | std_fs::perms::others_exec;
+ constexpr auto perms_0755 = perms_0555 | std_fs::perms::owner_write;
+ constexpr auto perms_0775 = perms_0755 | std_fs::perms::group_write;
+
+ QTest::addColumn<std_fs::perms>("originalPermissions");
+ QTest::addColumn<FileSystem::FolderPermissions>("folderPermissions");
+ QTest::addColumn<bool>("expectedResult");
+ QTest::addColumn<bool>("expectedPermissionsChanged");
+ QTest::addColumn<std_fs::perms>("expectedPermissions");
+
+ QTest::newRow("0777, readonly -> 0555, changed")
+ << std_fs::perms::all
+ << FileSystem::FolderPermissions::ReadOnly
+ << true
+ << true
+ << perms_0555;
+
+ QTest::newRow("0555, readonly -> 0555, not changed")
+ << perms_0555
+ << FileSystem::FolderPermissions::ReadOnly
+ << true
+ << false
+ << perms_0555;
+
+ QTest::newRow("0777, readwrite -> 0775, changed")
+ << std_fs::perms::all
+ << FileSystem::FolderPermissions::ReadWrite
+ << true
+ << true
+ << perms_0775;
+
+ QTest::newRow("0775, readwrite -> 0775, not changed")
+ << perms_0775
+ << FileSystem::FolderPermissions::ReadWrite
+ << true
+ << false
+ << perms_0775;
+
+ QTest::newRow("0755, readwrite -> 0755, not changed")
+ << perms_0755
+ << FileSystem::FolderPermissions::ReadWrite
+ << true
+ << false
+ << perms_0755;
+
+ QTest::newRow("0555, readwrite -> 0755, changed")
+ << perms_0555
+ << FileSystem::FolderPermissions::ReadWrite
+ << true
+ << true
+ << perms_0755;
+ }
+
+ void testSetFolderPermissionsExistingDirectory()
+ {
+ QFETCH(std_fs::perms, originalPermissions);
+ QFETCH(FileSystem::FolderPermissions, folderPermissions);
+ QFETCH(bool, expectedResult);
+ QFETCH(bool, expectedPermissionsChanged);
+ QFETCH(std_fs::perms, expectedPermissions);
+
+ bool permissionsDidChange = false;
+ QString fullPath = testDir.filePath("existingDirectory");
+ const auto stdStrPath = fullPath.toStdWString();
+
+ std_fs::permissions(stdStrPath, originalPermissions);
+
+ QCOMPARE(FileSystem::setFolderPermissions(fullPath, folderPermissions, &permissionsDidChange), expectedResult);
+
+ const auto newPermissions = std_fs::status(stdStrPath).permissions();
+ QCOMPARE(newPermissions, expectedPermissions);
+ QCOMPARE(permissionsDidChange, expectedPermissionsChanged);
+ }
+
+ void testSetFolderPermissionsNonexistentDirectory()
+ {
+ bool permissionsDidChange = false;
+
+ QString fullPath = testDir.filePath("nonexistentDirectory");
+
+ QCOMPARE(FileSystem::setFolderPermissions("nonexistentDirectory", FileSystem::FolderPermissions::ReadOnly, &permissionsDidChange), false);
+ QCOMPARE(permissionsDidChange, false);
+ }
+#endif
+};
+
+QTEST_GUILESS_MAIN(TestFileSystem)
+#include "testfilesystem.moc"